import multiprocessing
import time

def processExecCallback(code, local, key):
    exec(code, {}, local)
    return local[key]

class ProcessExecutor:
    def __init__(self):
        self.task_id = 0
        self.process = None
        self.task_queue = multiprocessing.Queue()
        self.result_queue = multiprocessing.Queue()

    def exec(self, code, globals, local):
        return self.run_task(processExecCallback, code, globals, local)


    def run_task(self, task, arg1, arg2, arg3, timeout=10):
        # 如果没有子进程，或子进程已退出，创建并启动一个新的子进程
        if self.process is None or not self.process.is_alive():
            self.process = multiprocessing.Process(target=self.task_worker)
            self.process.start()

        # 生成唯一的任务ID
        self.task_id = self.task_id + 1
        task_uuid = self.task_id
        self.task_queue.put((task_uuid, task, arg1, arg2, arg3))

        start_time = time.time()

        while True:
            # 检查结果队列中是否有当前任务的结果
            result_uuid, result, error = self.result_queue.get(timeout= timeout - time.time() + start_time)
            if result_uuid == task_uuid:
                if error:
                    raise error
                return result
            # 如果任务超时，终止子进程并抛出异常
            if time.time() - start_time > timeout:
                self.process.terminate()
                self.process = None
                raise TimeoutError("Task timed out")
            # 如果子进程意外退出，抛出异常
            if not self.process.is_alive():
                self.process = None
                raise Exception("Subprocess died unexpectedly")

    def task_worker(self):
        while True:
            g = self.task_queue.get()
            if g == None:
                break
            task_uuid, task, arg1, arg2, arg3 = g
            if task_uuid == 'quit' or task_uuid == None:
                break 
            try:
                result = task(arg1, arg2, arg3)
                self.result_queue.put((task_uuid, result, None))
            except Exception as e:
                self.result_queue.put((task_uuid, None, e))


    def quit(self):
        self.task_queue.put(None)
        if self.process != None :
            self.process.terminate()

